package datacache

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"time"

	"gitee.com/haodreams/golib/logs"
	"gitee.com/haodreams/libs/routine"

	"gitee.com/haodreams/libs/ee"
)

// Cache 缓存文件
type Cache struct {
	path    string
	lock    *sync.RWMutex
	mapItem map[int32]*Item
	routine.Routine
	saveFile   *os.File
	createTime int64
	saveDays   int //当天数为0时不开启缓存
}

// Reset 复位
func (me *Cache) Reset() {
	me.lock.Lock()
	me.mapItem = map[int32]*Item{}
	me.lock.Unlock()
}

// Setup 初始化设置
func (me *Cache) Setup() (err error) {
	me.lock = new(sync.RWMutex)
	me.mapItem = map[int32]*Item{}
	me.saveDays = 10
	me.SetSavePath("cache")
	me.AddFuncAfterStart(me.afterStart)
	me.AddFuncBeforStop(me.beforeStop)
	go me.autoDeleteTimeoutFile()
	return
}

// afterStart 启动前执行的操作
func (me *Cache) afterStart() (err error) {
	go me.autoFlush()
	return
}

// beforeStop 停止前执行的操作
func (me *Cache) beforeStop() (err error) {
	return
}

// GetSavePath 获取保存的路径
func (me *Cache) GetSavePath() string {
	return me.path
}

// SetSavePath 设置数据保存的路径
func (me *Cache) SetSavePath(path string) (err error) {
	path = strings.Replace(path, "\\", "/", -1)
	if !strings.HasSuffix(path, "/") {
		path += "/"
	}
	me.path = path
	return os.MkdirAll(me.path, 0775)
}

// SetSaveDays 设置保存的天数
func (me *Cache) SetSaveDays(days int) {
	if days < 0 {
		days = 0
	}
	me.saveDays = days
	logs.Info("历史数据保存天数", days)
}

// Update 更新内存数据
func (me *Cache) Update(id int32, value float64, status byte, time int64) {
	if me.saveDays == 0 {
		return
	}
	me.lock.RLock()
	item, ok := me.mapItem[id]
	me.lock.RUnlock()
	if !ok {
		item = new(Item)
		item.ID = id
		item.Deadband = DeadbandConst
		item.Status = 0xff
		me.lock.Lock()
		me.mapItem[id] = item
		me.lock.Unlock()
	}
	item.Value = value
	item.Status = status
	item.Time = int32(time)
}

// UpdateWithDeadband 更新内存数据
func (me *Cache) UpdateWithDeadband(id int32, value float64, status byte, time int64, deadband float64) {
	if me.saveDays == 0 {
		return
	}
	me.lock.RLock()
	item, ok := me.mapItem[id]
	me.lock.RUnlock()
	if !ok {
		item = new(Item)
		item.ID = id
		item.Deadband = deadband
		item.Status = 0xff
		me.lock.Lock()
		me.mapItem[id] = item
		me.lock.Unlock()
	}
	item.Deadband = deadband
	item.Value = value
	item.Status = status
	item.Time = int32(time)
}

func (me *Cache) save(t int64, all bool) (err error) {
	if me.saveDays == 0 {
		return
	}
	if me.saveFile != nil {
		t -= t%600 + 120
		me.lock.RLock()
		defer me.lock.RUnlock()
		ok := false
		if all {
			for _, item := range me.mapItem {
				if item != nil {
					if item.Time < int32(t) {
						continue
					}
					ok = true
					_, e := me.saveFile.Write(item.Encode())
					if e != nil {
						err = ee.New(e).Once2(logs.Errorf)
						me.saveFile.Close()
						me.saveFile = nil
						return
					}
				}
			}
		} else {
			for _, item := range me.mapItem {
				if item != nil {
					if item.Time < int32(t) {
						continue
					}
					if !Deadband(item.LastValue, item.Value, item.Deadband) || item.LastStatus != item.Status {
						item.LastValue = item.Value
						item.LastStatus = item.Status
						_, e := me.saveFile.Write(item.Encode())
						if e != nil {
							err = ee.New(e).Once2(logs.Errorf)
							me.saveFile.Close()
							me.saveFile = nil
							return
						}
						ok = true
					}
				}
			}
		}
		if ok {
			me.saveFile.Sync()
		}
	}
	return
}

// GetItems 获取一个文件的全部点
func (me *Cache) GetItems(t int64) (items []*Item, err error) {
	ft := time.Unix(t-t%600, 0)
	path := fmt.Sprintf("%s%s/%s", me.path, ft.Format("20060102"), ft.Format("1504.dat"))
	//logs.Info("path:", path)
	f, e := os.Open(path)
	if e != nil {
		err = ee.New(e)
		return
	}
	defer f.Close()

	buf := bufio.NewReader(f)
	line := make([]byte, 17)
	for n, err := io.ReadFull(buf, line); n == 17 && err == nil; n, err = io.ReadFull(buf, line) {
		item := new(Item)
		e := item.Decode(line)
		if e != nil {
			e.Once2(logs.Errorf)
			continue
		}
		items = append(items, item)
	}
	return
}

// flush 保存内存中的数据到文件
func (me *Cache) flush() (err error) {
	if me.saveDays == 0 {
		return
	}
	t := time.Now()
	if (t.Unix()-600) >= me.createTime || (me.createTime > t.Unix()) {
		if me.saveFile != nil {
			//保存一次全部数据
			me.save(t.Unix(), true)
			me.saveFile.Close()
			me.saveFile = nil
			return
		}
		var e error
		t = time.Unix(t.Unix()-t.Unix()%600, 0)
		path := fmt.Sprintf("%s%s/", me.path, t.Format("20060102"))
		os.Mkdir(path, 0775)
		path += t.Format("1504.dat")
		me.saveFile, e = os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		//logs.Info(t.Unix(), path, me.path)
		if e != nil {
			err = ee.New(e).Once2(logs.Errorf)
			me.saveFile = nil
			return
		}
		//保存一次全部数据
		me.save(t.Unix(), true)
		me.createTime = t.Unix()
	} else {
		me.save(t.Unix(), false)
	}

	return
}

// autoFlush 保存内存中的数据到文件
func (me *Cache) autoFlush() {
	if me.saveDays == 0 {
		return
	}

	for me.IsRunning() {
		me.flush()
		//time.Sleep(time.Millisecond * 800)
		time.Sleep(time.Second)
	}
}

func (me *Cache) autoDeleteTimeoutFile() {
	if me.saveDays == 0 {
		return
	}
	t := time.NewTicker(time.Second * 7200)
	//t := time.NewTicker(time.Second * 30)
	for range t.C {
		st := time.Now().Unix() - int64(86400*time.Duration(me.saveDays))
		timeout := time.Unix(st, 0).Format("20060102")
		fs, err := ioutil.ReadDir(me.path)
		if err != nil {
			continue
		}
		for _, f := range fs {
			if f.IsDir() {
				if f.Name() < timeout {
					err = os.RemoveAll(me.path + f.Name())
					if err != nil {
						logs.Error(err.Error())
					}
				}
			}
		}
	}
}
